﻿#include "modbusrtu.h"

ModbusRTU::ModbusRTU(QListWidget *listwidget) :
    MessageListWidget(listwidget)
{

}

void ModbusRTU::ModbusRTU_03(QByteArray &cmd, uint8_t *addr, uint16_t len) //03命令组帧
{
    uint16_t crc;

    cmd[0] = modbusAddr;
    cmd[1] = 0x03;
    cmd[2] = addr[0];
    cmd[3] = addr[1];
    cmd[4] = len>>8;
    cmd[5] = len;
    crc = GetCRCModBus((uint8_t *)cmd.data(), 6);
    cmd[6] = crc;
    cmd[7] = crc>>8;
}

void ModbusRTU::ModbusRTU_10(QByteArray &cmd, uint8_t *addr, uint16_t len, uint8_t *data) //10命令组帧
{
    uint16_t crc;

    cmd[0] = modbusAddr;
    cmd[1] = 0x10;
    cmd[2] = addr[0];
    cmd[3] = addr[1];
    cmd[4] = len>>8;
    cmd[5] = len;
    cmd[6] = len*2;

    for(int i=0;i<len*2;i++)
    {
        cmd[7+i] = data[i];
    }

    crc = GetCRCModBus((uint8_t *)cmd.data(), 7+len*2);
    cmd[7+len*2] = crc;
    cmd[7+len*2+1] = crc>>8;
}

bool ModbusRTU::ModbusRTUSend(QSerialPort *port, QByteArray snd, QByteArray &rcv)  //发送命令
{
    if(port == nullptr)   // 健壮机制，检查串口是否正常
        return false;
    port->clear();

    //阻塞法 频繁读取时卡顿明显
    //        port->write(snd);
    //        QApplication::processEvents();
    //        if(port->waitForBytesWritten(responDelayTime)){
    //            ShowSndMessage(snd);
    //            QApplication::processEvents();
    //            if(port->waitForReadyRead(responDelayTime)){
    //                rcv = port->readAll();
    //                QApplication::processEvents();
    //                while(port->waitForReadyRead(40))
    //                    rcv += port->readAll();
    //                if(rcv.isEmpty())
    //                    return false;
    //                ShowRcvMessage(rcv);
    //            }
    //        }

    //挂起GUI等待读取
    port->write(snd);
    ShowSndMessage(snd);

    MyDelay(responDelayTime);

    rcv = port->readAll();
    if(rcv.isEmpty())
        return false;
    ShowRcvMessage(rcv);

    //功能码
    if(snd[1] != rcv[1])
        return false;
    //        if(rcv[1] == 0x3 && snd[3]*0x100)

    //CRC
    return CheckCRCModbus((uint8_t *)rcv.data(), rcv.size());
}

void ModbusRTU::ModbusRTU_03_GetValue(QSerialPort *port, uint8_t *addr, uint16_t len, vector<uint8_t> &value)
{
    QByteArray snd, rcv;
    ModbusRTU_03(snd, addr, len);
    ModbusRTUSend(port, snd, rcv);

    while(value.size())
        value.pop_back();

    int size = (uint8_t)rcv[2];

    for(int i=0;i<size;i++)
        value.push_back((uint8_t)rcv[3+i]);
}

void ModbusRTU::changeResponDelayTime(int nums)
{
    responDelayTime = nums;
}

QByteArray ModbusRTU::AutoWriteModbus(int nums, QByteArray cmd)
{
    //自动编写报文
    //        QFile file("./out.txt");
    //        if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    //            return;

    //        QTextStream out(&file);
    //        cmd.clear();

    //        cmd[0] = 0x00,
    cmd[1] = 0x03,
            cmd[2] = nums*2;

    qint64 t = QDateTime::currentMSecsSinceEpoch();

    qsrand(t);
    for(int j=0;j<nums;j++)
    {
        cmd[3+j*2] = 0;
        cmd[4+j*2] = qrand()%12;
    }

    uint16_t crc = GetCRCModBus((uint8_t *)cmd.data(), nums*2+3);
    cmd[nums*2+3] = crc%256;
    cmd[nums*2+4] = crc/256;
    //        QString str = sp.ShowHex(cmd);
    //        out << str << endl;

    return cmd;
}

void ModbusRTU::ChangeModbusAddr(uint8_t newAddr)
{
    modbusAddr = newAddr;
}

void ModbusRTU::ShowSndMessage(QByteArray snd)
{
    QString cmd = sp.ShowHex(snd);
    MessageListWidget->addItem(QDateTime::currentDateTime().toString("[yyyy-MM-dd hh:mm:ss] :")
                               + cmd);
    MessageListWidget->item(MessageListWidget->count()-1)->setTextColor(Qt::blue);
    MessageListWidget->setCurrentRow(MessageListWidget->count()-1);
}

void ModbusRTU::ShowRcvMessage(QByteArray rcv)
{
    QString cmd = sp.ShowHex(rcv);
    MessageListWidget->addItem(QDateTime::currentDateTime().toString("[yyyy-MM-dd hh:mm:ss] :")
                               + cmd);
    MessageListWidget->item(MessageListWidget->count()-1)->setTextColor(Qt::red);
    MessageListWidget->setCurrentRow(MessageListWidget->count()-1);
}

void ModbusRTU::MyDelay(int msecs) //ms延时
{
    qint64 n = QDateTime::currentMSecsSinceEpoch();
    qint64 now;
    do{
        now = QDateTime::currentMSecsSinceEpoch();
        QApplication::processEvents();
    } while (now - n < msecs);//
}

uint16_t ModbusRTU::CalcCRCModBus(uint8_t cDataIn, uint16_t wCRCIn)
{
    u16 wCheck = 0;
    u8 i;
    wCRCIn = wCRCIn ^ cDataIn;

    for( i = 0; i < 8; i++)
    {
        wCheck = wCRCIn & 1;
        wCRCIn = wCRCIn >> 1;
        wCRCIn = wCRCIn & 0x7fff;

        if(wCheck == 1)
        {
            wCRCIn = wCRCIn ^ 0xa001;
        }
        wCRCIn = wCRCIn & 0xffff;
    }

    return wCRCIn;
}

uint16_t ModbusRTU::GetCRCModBus(uint8_t *pDataIn, uint8_t iLenIn)
{
    u16 wHi = 0;
    u16 wLo = 0;
    u16 wCRC;
    u8 i;
    wCRC = 0xFFFF;

    for ( i = 0; i < iLenIn; i++)
    {
        wCRC = CalcCRCModBus(*pDataIn, wCRC);
        pDataIn++;
    }

    wHi = wCRC / 256;
    wLo = wCRC % 256;
    wCRC = (wHi << 8) | wLo;

    return(wCRC);
}

bool ModbusRTU::CheckCRCModbus(uint8_t *pDataIn, uint8_t iLenIn)
{
    u16 wHi = 0;
    u16 wLo = 0;
    u16 wCRC;
    u8 i;
    wCRC = 0xFFFF;
    u8 *temp = pDataIn;

    for ( i = 0; i < iLenIn-2; i++)
    {
        wCRC = CalcCRCModBus(*pDataIn, wCRC);
        pDataIn++;
    }

    wHi = wCRC / 256;
    wLo = wCRC % 256;

    if(wLo == temp[iLenIn-2] && wHi == temp[iLenIn-1])
        return true;
    return false;
}
